#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <assert.h>
#include <string.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#define NUM 100
#define OPT_NUM 64
char LineCommand[NUM];
char* myargv[OPT_NUM];//指针数组，用于记录切割出来的字符串
//确定是哪种重定向的宏
#define NONE_REDIR 0
#define INPUT_REDIR 1
#define OUTPUT_REDIR 2
#define APPEND_REDIR 3
int redirType=NONE_REDIR;//重定向种类
char* redirFile=NULL;//所访问的文件的起始位置
//过滤空格的宏函数
#define trimSpace(start) do{\
    while(isspace(*start))\
        ++start;\
}while(0) 


void commandCheck(char* commands)//扫描字符串
{
    assert(commands);
    char* start=commands;
    char* end=commands+strlen(commands);//end指向\0
    while(start<end)
    {
        if(*start=='>')//有两种情况，第一种是输出重定向，第二种是追加重定向
        {
            *start='\0';
            ++start;
            if(*start=='>')//如果跳过一个字符，后一个字符仍为'>',则为追加重定向
            {
                redirType=APPEND_REDIR;
                start++;
            }
            else//输出重定向 
            {
                redirType=OUTPUT_REDIR;
            }
            //过滤空格
            trimSpace(start);
            redirFile=start;
            break;
        }
        else if(*start=='<')
        {
            *start='\0';
            ++start;
            //过滤空格
            trimSpace(start); 
            //填写重定向信息
            redirType=INPUT_REDIR;//重定向种类
            redirFile=start;//重定向起始地址
            break;
        }
        else//如果输入命令中没有>、>>、<，start会一直++跳出循环 
        {
            ++start;
        }
    }
}
int main()
{   
    while(1)
    {
        //初始化
        redirType=NONE_REDIR;//重定向种类
        redirFile=NULL;//所访问的文件的起始位置
        errno=0;
        printf("用户名@主机名 当前路径:");
        fflush(stdout);
        //获取输入内容
        char* s=fgets(LineCommand,sizeof(LineCommand),stdin);
        assert(s!=NULL);
        (void)s;
        //清除最后一个\n
        LineCommand[strlen(LineCommand)-1]=0;
        //"ls -a -l -i > log.txt" 分割为"ls -a -l -i"和"log.txt" 
        //"ls -a -l -i << log.txt" 分割为"ls -a -l -i"和"log.txt" 
        //"cat < log.txt"分割为"cat"和"log.txt"
        commandCheck(LineCommand);//父进程判断输入命令是否是重定向命令
        //字符串切割
        myargv[0]=strtok(LineCommand," ");
        int i=1;
        if(myargv[0]!=NULL&&strcmp(myargv[0],"ls")==0)
        {
            myargv[i++]=(char*)"--color=auto";
        }
        while(myargv[i++]=strtok(NULL," "));//如果切割完毕，strtok会返回NULL，刚好myargv[end]需要等于NULL
        if(myargv[0]!=NULL&&strcmp(myargv[0],"cd")==0)
        {
            if(myargv[1]!=NULL)
            {
               chdir(myargv[1]);//使用chdir改变父进程的当前路径
               continue;
            }
        }
        //测试是否成功
#ifdef DEBUG 
        for(i=0;myargv[i];++i)
        {
            printf("myargv[%d]:%s\n",i,myargv[i]);
        }
#endif
        //执行命令
        pid_t id=fork();//创建子进程，让子进程去执行命令
        assert(id!=-1);
        if(id==0)
        {
            switch(redirType)//子进程的重定向并不会影响父进程
            {
                case NONE_REDIR://指令中没有重定向，那什么都不做
                    break;
                case INPUT_REDIR://输入重定向
                    {

                        int fd=open(redirFile,O_RDONLY);
                        if(fd<0)
                        {
                            perror("open");
                            exit(errno);
                        }
                        //重定向文件已被成功打开
                        dup2(fd,0);
                    }
                    break;
                case OUTPUT_REDIR://输出重定向
                case APPEND_REDIR://追加重定向
                    {
                        int flags=O_WRONLY | O_CREAT;
                        if(redirType==APPEND_REDIR)
                        {
                            flags|=O_APPEND;
                        }
                        else 
                        {
                            flags|=O_TRUNC;
                        }
                        int fd=open(redirFile,flags,0666);
                        if(fd<0)
                        {
                            perror("open");
                            exit(errno);
                        }
                        //重定向文件已被成功打开
                        dup2(fd,1);
                    }
                    break;
                default:
                    perror("REDIR");
                    break;
            }
            execvp(myargv[0],myargv);//进程程序替换的时候，不会影响进程打开的重定向文件，因为程序替换不会影响内核的数据结构
            exit(1);
        }
        waitpid(id,NULL,0);
    }
    return 0;
}
